home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Internet Info 1994 March
/
Internet Info CD-ROM (Walnut Creek) (March 1994).iso
/
networking
/
mail
/
mh
/
contrib
/
9210
/
stacknews.shar
/
stacknews
next >
Wrap
Text File
|
1992-10-22
|
13KB
|
490 lines
#!/usr/bin/perl
#
# stacknews - create and maintain a folder stack for reading news
#
# This program builds a folder stack (separate from the MH folder stack)
# of news groups to be read. It prints the path to the appropriate
# MH context file. A separate MH context is kept for each news group.
# This should speed up certain MH operations and avoids the problem of
# having news group folders in your main MH context.
#
# Usage:
# setenv MHCONTEXT \
# `stacknews [-(no)check] [-(no)clear] [-(no)debug] [-(no)list]
# [-(no)next] [-(no)push] [-(no)rigor] group ...`
#
# Options:
# -check find the first group with new articles
# -clear clear the folder stack
# -debug turn on debugging
# -list list the folder stack
# -next pop the folder stack
# -nocheck push all groups unconditionally
# -noclear do not clear the folder stack
# -nodebug operate in normal (non-debug) mode
# -nolist do not list the folder stack
# -nonext do not pop the folder stack
# -nopush do not push groups onto the folder stack
# -norigor use defaults or the environment ONLY
# -push push groups onto the folder stack
# -rigor check the MH profile (the usual MH stuff)
#
# Defaults: -check -noclear -nodebug -nonext -push -rigor
#
# Any groups on the command line are pushed onto the stack.
# If no groups are specified, then all default groups are
# pushed onto the stack.
#
# Mutually exclusive options: -clear, -list, -next, -push
#
# Some useful code to include in your .cshrc file:
#
# if (! $?stacknews) then
# set stacknews = "stacknews"
# endif
# alias sninvo 'setenv MHCONTEXT `$stacknews \!*`'
# alias news 'set stacknews = "stacknews" ; sninvo'
# alias fnews 'set stacknews = "stacknews.f" ; sninvo'
# alias g 'setenv MHCONTEXT `$stacknews -next \!*`'
# alias q 'folder -fast last > /dev/null ; g'
# alias u 'setenv MHCONTEXT `$stacknews -nopush \!*`'
# alias z '$stacknews -clear \!*; unsetenv MHCONTEXT'
# alias nglist '$stacknews -list \!*'
#
# The code above defines the following commands:
# g -- Go to the next news group.
# news -- Read new news groups.
# nglist -- See the current state of the news stack.
# q -- Set the last article to be the current article
# -- and go to the next news group.
# sninvo -- Utility alias to invoke stacknews.
# u -- Set ("update") another shell's MHCONTEXT ...
# but unfortunately doesn't handle the case
# of an alternative set of news groups, e.g.
# as read by stacknews.f
# z -- Stop reading news altogether.
#
#
# Environment variables:
# HOME path of home directory
# MH path of MH profile file
# MHCONTEXT path of current MH context file
# NEWSARTS path of top directory containing news articles
# NEWSCONTEXTS path of directory containing folder contexts
# NEWSGROUPS list of colon-separated news groups to read
#
# MH profile components:
# context path of MH context file
# path path of MH directory
# (prog) default options
# (prog)-arts path of top directory containing news articles
# (prog)-contexts path of directory containing folder contexts
# (prog)-defaults path of file containing list of news groups
# (prog)-groups list of comma-separated news groups to read
#
# Note: "(prog)" above is the invocation name of the program;
# MH profile components might be named stacknews-defaults,
# stacknews-groups, and stacknews-arts, if the program is invoked
# as "stacknews".
#
# Files and Directories:
# $HOME/.mh_profile default MH profile
# $HOME/.news-contexts default news folder contexts directory
# $HOME/.news-contexts/Stack news folder stack
# $HOME/.news-defaults list of default news groups
# $HOME/Mail default MH directory
# $HOME/Mail/context default MH context file
#
# Author: Jerry Sweet <jns@fernwood.mpk.ca.us>
# Taken from my old, similar, but much slower, "news" program.
#
# $Header: /q2/uh/jsweet/src/mh-front/RCS/stacknews,v 1.6 1992/06/11 18:24:52 jsweet Exp $
#
# Subprograms
#
sub relative_to_absolute {
local($_) = @_;
if ($_ !~ /^\//) {
$_ = sprintf("%s%s%s", $mhroot, $_ ? '/' : '', $_);
}
if ($_ !~ /^\//) {
$_ = sprintf("%s/%s", $ENV{'HOME'}, $_);
}
$_;
}
sub destroy_cur {
local($dir) = @_;
local($sequence_file) = "$dir/.mh_sequences";
unlink $sequence_file if ($update_cur && -e $sequence_file);
# This is the only known way (in MH 6.6 at least) to reset cur if new
# messages have arrived in a folder (via rcvstore) after all messages
# were removed or refiled.
}
#
# Main
#
umask 077; # This should be adjustable, but isn't at the moment.
($prog = $0) =~ s%.*/%%;
$newsarts = defined $ENV{'NEWSARTS'} ? $ENV{'NEWSARTS'} : '/usr/spool/news';
$HOME = $ENV{'HOME'} ;
$profile = defined $ENV{'MH'} ? $ENV{'MH'} : "$HOME/.mh_profile";
$mhroot = "$HOME/Mail";
if (defined $ENV{'NEWSGROUPS'}) {
@groups = split(/:/, $ENV{'NEWSGROUPS'});
}
$contexts = defined $ENV{'NEWSCONTEXTS'} ?
$ENV{'NEWSCONTEXTS'} :
"$HOME/.news-contexts";
$mhcontext = defined $ENV{'MHCONTEXT'} ?
$ENV{'MHCONTEXT'} :
"$mhroot/context";
$check_folders = 1;
$rigor = 1;
$push = 1;
# Look for the -norigor option in the command line.
while ($_ = shift @ARGV) {
push(@Args, $_);
/^-norigor$/ && $rigor--;
/^-rigor$/ && $rigor++;
}
if ($rigor > 0) {
$ndfile = -e "$HOME/.news-defaults" ? "$HOME/.news-defaults" : '';
$profile_component = 'x-bogus';
if (! open(PROFILE, "<$profile")) {
print STDERR "$prog: can't open profile \"$profile\" - $!\n";
}
else {
while (<PROFILE>) {
/^(\S+):\s*/ && do {
($profile_component = $1) =~ tr/A-Z/a-z/;
$PROFILE_COMPONENT{$profile_component} = $';
};
/^[ \t]/ && do {
$PROFILE_COMPONENT{$profile_component} .= $_;
};
}
if (defined $PROFILE_COMPONENT{'path'}) {
$_ = $PROFILE_COMPONENT{'path'};
/^\s*(\S+)/ && do { $mhroot = $1; };
}
if (defined $PROFILE_COMPONENT{'context'}) {
$_ = $PROFILE_COMPONENT{'context'};
/^\s*(\S+)/ && do {
$mhcontext = $1;
if ($mhcontext !~ /^\//) {
$mhcontext = "$mhroot/$mhcontext";
}
};
}
if (defined $PROFILE_COMPONENT{$prog}) {
$_ = $PROFILE_COMPONENT{$prog};
@Args = (split, @Args);
}
if (defined $PROFILE_COMPONENT{"$prog-arts"}) {
$_ = $PROFILE_COMPONENT{"$prog-arts"};
/^\s*(\S+)/ && do { $newsarts = $1; };
}
if (defined $PROFILE_COMPONENT{"$prog-contexts"}) {
$_ = $PROFILE_COMPONENT{"$prog-contexts"};
/^\s*(\S+)/ && do {
$contexts = $1;
if ($contexts !~ /^\//) {
$contexts = "$mhroot/$contexts";
}
};
}
if (defined $PROFILE_COMPONENT{"$prog-defaults"}) {
$_ = $PROFILE_COMPONENT{"$prog-defaults"};
/^\s*(\S+)/ && do {
$ndfile = $1;
if ($contexts !~ /^\//) {
$contexts = "$mhroot/$contexts";
}
};
}
if (defined $PROFILE_COMPONENT{"$prog-groups"}) {
$_ = $PROFILE_COMPONENT{"$prog-groups"};
@profile_groups = split(/\s*,\s*/);
}
}
}
# Evaluate the switches:
while ($_ = shift @Args) {
/^-check$/ && $check_folders++;
/^-clear$/ && $clear_stack++;
/^-debug$/ && $debug++;
/^-list$/ && $list++;
/^-next$/ && $next++;
/^-nocheck$/ && $check_folders--;
/^-noclear$/ && $clear_stack--;
/^-nodebug$/ && $debug--;
/^-nolist$/ && $list--;
/^-nonext$/ && $next--;
/^-nopush$/ && $push--;
/^-push$/ && $push++;
/^[^-]/ && push(@switch_groups, $_);
}
if (! -d $contexts) {
if (! mkdir($contexts, 0700)) {
print STDERR "$prog: can't mkdir \"$contexts\" - $!\n";
exit 1;
}
}
$folder_stack = "$contexts/Stack";
if ($clear_stack > 0) {
@groups = ();
}
else {
# Read the folder stack:
if (-e $folder_stack) {
if (! open(STACK, "<$folder_stack")) {
print STDERR
"$prog: can't open folder stack file \"$folder_stack\" - $!\n";
print "$mhcontext\n" unless ! $print_context;
exit 1;
}
foreach (<STACK>) {
next if /^#|^\s*$/;
push(@stack, split);
}
}
if ($list > 0) {
foreach (@stack) {
push(@group_list, $_);
}
if ($#group_list >= 0) {
print STDERR join(' ', @group_list), "\n";
}
exit 0;
}
}
shift(@stack) if ($next > 0); # If we saw -next, then pop the stack.
if ($clear_stack <= 0 && $next <= 0 && $push > 0) {
# Decide on the news groups at which to look:
if ($#switch_groups < 0 && $#profile_groups >= 0) {
push(@groups, @profile_groups);
# Select groups from the (prog)-groups: component.
}
if ($#groups < 0 && $#switch_groups < 0 && $ndfile) {
# Look at the .news-defaults file:
if (! open(NDFILE, $ndfile)) {
print STDERR "$prog: can't open news defaults file \"$ndfile\" - $!\n";
print "$mhcontext\n" unless ! $print_context;
exit 1;
}
while (<NDFILE>) {
next if /^#|^\s*$/;
split;
push(@groups, @_);
}
}
# Push news groups onto the folder stack:
push(@stack, @groups);
# Push groups from the command line onto the front of the stack:
foreach (reverse(@switch_groups)) {
unshift(@stack, $_);
}
}
if ($clear_stack <= 0 && $push > 0) {
# Find the first group for which new articles have arrived:
if ($check_folders > 0) {
select(STDERR);
$| = 1; # Force output of '.' as we look for a good group
select(STDOUT);
}
$newsarts_cur = $newsarts;
while ($#stack >= 0) {
($_ = $stack[0]) =~ s/\s+//g;
# An item beginning with '+' specifies a folder instead of
# a news group.
if (/^\+/) {
$_ = $';
$group = $_;
$dir = &relative_to_absolute($_);
$folder = $_;
s%/%.%g;
$cur_context = "$contexts/FOLDER.$_";
# Bug: +foo.bar will have the same context as +foo/bar.
$update_cur = 1;
}
else {
$group = $_;
s%\.%/%g;
$dir = "$newsarts_cur/$_";
$folder = $dir;
$cur_context = "$contexts/$group";
$update_cur = 0;
}
# print STDERR "group = \"$group\"\n",
# "dir = \"$dir\"\n",
# "folder = \"$folder\"\n",
# "ctxt = \"$cur_context\"\n",
# "stack = ", join(' ', @stack), "\n"
# ;
last if ($check_folders <= 0);
if (! -d $dir) {
print STDERR "\nno such news group as $group\n";
shift @stack;
next;
}
$ENV{'MHCONTEXT'} = $cur_context;
$finfo = `folder +$folder`;
if ($? >> 8) {
print STDERR "\nfolder change to +$folder failed\n";
shift @stack;
next;
}
print(STDERR $finfo) if ($debug > 0);
if ($finfo =~ /\s+has\s+no\s+/) {
# There are no messages in this folder, so ensure that the cur
# sequence is destroyed and go on to the next folder.
&destroy_cur($dir);
print STDERR '.';
$dots++;
shift @stack;
next;
}
if ($finfo =~ /cur=\s*(\d+)/) {
$cur = $1;
}
else {
# There are messages in here, but there's no cur message apparent,
# so reset the cur sequence.
&destroy_cur($dir);
$cur = 0;
}
if ($finfo =~ /message.*\(\s*(\d+)-\s*(\d+)\s*\)/) {
$lastm = $2;
if ($cur >= $lastm) {
# We don't have any new messages in this folder, so go on to
# the next one. (Bug: we should check for an "unseen" sequence.)
print STDERR '.';
$dots++;
shift @stack;
next;
}
else {
last; # Found a live one.
}
}
} # end while
if ($dots > 0) {
print STDERR "\n";
select(STDERR);
$| = 0;
select(STDOUT);
}
}
# Rewrite the folder stack file:
if ($clear_stack > 0) {
@stack = ();
}
if ($push > 0) {
if (! open(STACK, ">$folder_stack")) {
print STDERR
"$prog: can't rewrite folder stack file \"$folder_stack\" - $!\n";
print "$mhcontext\n" unless ! $print_context;
exit 1;
}
if ( (! print STACK join(' ', @stack)) || ! close(STACK) ) {
print STDERR
"$prog: can't write folder stack file \"$folder_stack\" - $!\n";
print "$mhcontext\n" unless ! $print_context;
exit 1;
}
}
if ($clear_stack > 0) {
exit 0;
}
# Tell the user what the state of the folder stack is:
if ($#stack >= 0) {
$_ = $stack[0];
if ($next <= 0) {
printf(STDERR "%d news group%s selected; topmost: %s\n",
$#stack + 1,
$#stack > 0 ? 's' : '',
$_);
}
else {
print STDERR "topmost: $_\n";
}
if (/^\+/) {
$_ = $';
s%/%.%g;
print "$contexts/FOLDER.$_\n";
}
else {
print "$contexts/$_\n";
}
}
else {
if ($next <= 0) {
print STDERR "no news groups selected\n";
}
print "$mhcontext\n" unless ! $print_context;
}
exit 0;